﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Linq.Expressions;
using System.Collections;

namespace Mvc.XForms.Utilities {
	public class ModelValidator<T> {
		public T ObjectToValidate { get; set; }
		public ModelStateDictionary ModelState { get; set; }
		public string Prefix { get; set; }

		public ModelValidator(T objToValidate, ModelStateDictionary modelState)
			: this(objToValidate, modelState, string.Empty) { }

		public ModelValidator(T objToValidate, ModelStateDictionary modelState, string prefix) {
			ObjectToValidate = objToValidate;
			ModelState = modelState;
			Prefix = prefix;
		}

		public virtual void Rule(Expression<Func<T, object>> bind, bool rule, string errorMessage) {
			if (rule)
				Error(bind, errorMessage);
		}

		public virtual void String(Expression<Func<T, object>> bind, Func<string, bool> rule, string errorMessage) {
			var objAsString = bind.Compile()(ObjectToValidate).ToString();
			var ruleAsBool = rule(objAsString);

			Rule(bind, ruleAsBool, errorMessage);
		}

		public virtual void RequiredString(Expression<Func<T, object>> bind, string errorMessage) {
			String(bind, str => str.Length == 0, errorMessage);
		}

		public virtual void Error(Expression<Func<T, object>> bind, string errorMessage) {
			var bindStr = bind.Body.ExtractID();
			var key = string.IsNullOrEmpty(Prefix) ? bindStr : string.Concat(Prefix, ExpressionHelper.Separator, bindStr);
			Error(key, errorMessage);
		}

		public virtual void Error(string key, string errorMessage) {
			ModelState.AddModelError(key, errorMessage);
		}

		public void Enumerable<TNew>(Expression<Func<T, IEnumerable<TNew>>> bind, Action<TNew, ModelValidator<TNew>> validationAction) {
			var listToValidate = bind.Compile()(ObjectToValidate);

			var bindStr = bind.Body.ExtractID();
			var prefix = string.IsNullOrEmpty(Prefix) ? bindStr : string.Concat(Prefix, ExpressionHelper.Separator, bindStr);

			listToValidate.ForEach((obj, i) => {
				var indexedPrefix = string.Concat(prefix, ExpressionHelper.IndexSeparator, i);
				var validator = new ModelValidator<TNew>(obj, ModelState, indexedPrefix);
				validationAction(obj, validator);
			});
		}
	}
}
